package books.new_to_old

/**
 * 泛型
 *
 */
fun main(args: Array<String>) {

    //1.类型上界,T必须实现Comparable
    fun <T : Comparable<T>> gt(t: T) {
    }

    //2.java与kotlin不支持型变，
    // 型变包括协变与逆变，相当于<? extend A>和<? super B>
    //Producer-Extends-out-get, Consumer-Super-in-写入-set
    //协变 out == <? extend A>,逆变 in == <? super B>
    //通配符上限（泛型协变〉意味着从中取出（ out ）对象是安全的，但传入对象（ in ）则
    //不可靠。
    //通配符下限（泛型逆变）意味着向其中传入（ in ）对象是安全的，但取出对象（ out)
    //则不可靠。
    //Kotlin 利用上面两个规律，抛弃了泛型通配符语法，而是利用 in out 来让泛型支持型变。

    //3.运行时泛型擦除
    // 首先，找到用来替换类型参数的具体类。这 具体类一般是 Object 。如果指定了
    //类型参数的上界的话， 则使用这个上界。
    //其次， 把代码中的类型参数都替换成具体的类。同 时去 出现的类型声明，即去
    //的内容 例如， T get（） 就变成了 Object get(), List<String＞就变成了 List
    //最后，根据需要生成 些桥接方法。这是由于擦除了类型之后的类可能缺少某些
    //必须的方法。这个时候就由编译器来动态生成这些方法

    //4.可以省略右边<>
    val list1: ArrayList<Int> = ArrayList()

    //5.声明处型变
    val user1 = User1("你好")
    var user1Any = User1(Any())
    user1Any = user1
    user1.test()

    var user2 = User2<String>()
    val user2Any = User2<Any>()
    user2 = user2Any
    user2.test("我不好")


    //6.使用处型变
    copy1(arrayOf(1, 2), arrayOf("a", "b"))

    var numArr1: Array<out Number> = arrayOf(1, 2, 3, 4, 0.66)
//    numArr1.set(0, 0.67)
    val intArr1 = arrayOf(1, 2, 3)
    numArr1 = intArr1


    copy2(arrayOf("aa", StringBuilder("bb"), StringBuffer("cc")), arrayOf("dd"))
    var intArr2: Array<in Int> = arrayOf(1, 2)
    val numArr2 = arrayOf(1, 0.34)
    intArr2 = numArr2

    //7.星号投影,不具备型变的泛型相当于java中不带泛型的方式
    val stringArr = arrayOf("aa", StringBuilder("bb"), StringBuffer("cc"), null) // 自动推断Array<*> == Array<Any?>
    val list2: ArrayList<*> = arrayListOf(1, 2)


    //T值不确定的情况下
    //当声明Fo<T>类,Fo<*> == Fo<out Any?>
    //当声明Fo<out T>类,Fo<*> == Fo<out Any?>
    //当声明Fo<in T>类,Fo<*> == Fo<in Nothing>,不能写入值
    //当声明Fo<in U,out T>类,Fo<*,*> == Fo<in Nothing,out Any?>

    println("你好吗") then println("我不好") then println("我怎么知道你不好")

    //9.具体化类型参数
    //Kotin 允许在内联函数（使用 inline 修饰的函数）中使用 reified 修饰泛型形 ，这样即可
    //将该泛型形参变成具体化的类型参数。 一旦将泛型形参变成具体化的类型参数，接下来在
    //该函数中就可以像使用普通类型 样使用该类型参数，包括使用 is as 等运算符
    findData("叫我宝宝")

    //10.设置类型形参的上限
    val user3 = User3(666)
    user3.t = 999
}

//与<out T> 的区别可以set  相当于java的<T extend Number>
class User3<T : Number>(var t: T)

//多个上界用where
class User4<T> where T : Comparable<T>, T : Cloneable


inline fun <reified T> findData(t: T) {
    if (t is String) {
        println("我是一个字符串$t")
    }
}


//8.扩展函数和泛型函数
infix fun <T> T.then(t: T): T {
    return t
}


private fun copy1(from: Array<out Any>, dest: Array<Any>) {
}

private fun copy2(from: Array<in String>, dest: Array<String>) {

}


/**
 * 如果泛型只需要出现在方法的返回值声明中（不出现在形参声明中〉，那么该方法就
只是取出泛型对象，因此该方法就支持泛型协变（相当于通配符上限〉：如果一个类
的所有方法都支持泛型协变，那么该类的泛型参数可使用 out 修饰。
 */
//协变,成员变量不能使用var,成员方法不能使用泛型做参数,只做返回值
class User1<out T>(val t: T) {

    fun test(): T {
        println("执行test方法")
        return t
    }
}

/**
 * 如果泛型只需要出现在方法的形参声明中（不出现在返回值声明中〉，那么该方法就
只是传入泛型对象，因此该方法就支持泛型逆变（相当于通配符下限〉：如果一个类
的所有方法都支持泛型逆变，那么该类的泛型参数可使用 in 修饰。
 */
//逆变
class User2<in T> {
    //T不能作为成员变量的类型

    //只能作为方法的参数,不能作为返回值
    fun test(t: T) {
        println("执行test方法=$t")
    }
}
